-
-
Notifications
You must be signed in to change notification settings - Fork 9.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: dont mutate Story prop types to optional #14550
Conversation
Both Partial and non-Partial make sense. The Partial was added intentionally with the mindset that users would likely want to supply a Props interface which is very strict. (all sorts of required fields) But then do this: import React from "react";
import { Story } from "@storybook/react";
interface ExampleProps {
requireMe: "please";
fine?: true;
}
const Template: Story<ExampleProps> = (args) => <div {...defaultProps} {...args}>example</div>;
export const Test = Template.bind({});
Test.args = {}; // no error, because the component will never be called without 'missing' properties (everything is optional) But a default stricter stance also makes sense... A user could very easily opt-in to all properties being optional by wrapping their type with I think this is an OK, change. Though it might be a breaking change. |
IF we were to make this change... import React from "react";
import { Story } from "@storybook/react";
interface ExampleProps {
requireMe: "please";
fine?: true;
}
- const Template: Story<ExampleProps> = (args) => <div {...defaultProps} {...args}>example</div>;
+ const Template: Story<Partial<ExampleProps>> = (args) => <div {...defaultProps} {...args}>example</div>;
export const Test = Template.bind({});
Test.args = {}; // no error, because the component will never be called without 'missing' properties (everything is optional) |
I think the issue here is that args are cascading. So let's say you have:
You could have stories that look like: import React from "react";
import { Story, Meta } from "@storybook/react";
interface ExampleProps {
require1: string;
require2: string;
}
export default {
title: 'foo';
args: { require1: 'haha' }
} as Meta<ExampleProps>
const Template: Story<ExampleProps> = (args) => <div {...args}>example</div>;
export const Test = Template.bind({});
Test.args = {
require2: 'hoho';
} Which is valid with the partial type but not valid with the strict type. |
Yeah the more I think about the more I think the Partial approach makes the most sense as a default. We could do this: import React from "react";
import { StrictStory } from "@storybook/react";
interface ExampleProps {
requireMe: "please";
fine?: true;
}
const Template: StrictStory<ExampleProps> = (args) => <div {...args}>example</div>;
export const Test = Template.bind({});
Test.args = {}; // error requireMe is undefined error should be visible here. This makes it easy to spot mistakes when you make em. |
Given the problems with this PR, are there cleaner solutions to help make this more strict? @ndelangen @Bram-Zijp |
As is, this can't be merged. But I'd be open to a |
I'm pretty sure that making the Story type strict will break a lot of people's codes. I think Partial makes sense, given how flexible the args are. I'd vouch for a secondary generic parameter in the Story type, with a sole purpose to toggle a strict mode. This way we introduce a desired feature that users can easily opt in: type StrictModes = 'loose' | 'strict'
interface Story<Args, StrictMode extends StrictModes = 'loose'> {
args: StrictMode extends 'strict' ? Args : Partial<Args>
}
const PartialTemplate: Story<ExampleProps> = (args) => <div {...args}>example</div>;
const StrictTemplate: Story<ExampleProps, 'strict'> = (args) => <div {...args}>example</div>; This way people can refer to tsdoc from Here's a playground with a working prototype Either that or Edit: thinking more about it, I guess people won't want to have a mix of |
I agree on that @yannbf. I'm lacking time and intel on the repo. Could someone pick this up? |
Hi Team Storybook :) |
☁️ Nx Cloud ReportCI is running/has finished running commands for commit 2ec5775. As they complete they will appear below. Click to see the status, the terminal output, and the build insights. 📂 See all runs for this branch ✅ Successfully ran 1 targetSent with 💌 from NxCloud. |
@kasperpeulen What should we do with this PR considering your recent and ongoing work around types and strictness? I'm sorry about that @Bram-Zijp It will likely mean your PR will most likely stay unmerged, and at some point will be closed. @kasperpeulen I'll leave it to you to decide what to do with this PR, let me know if you need any help to decide or to act upon it. |
@ndelangen I am happy that it got picked up. I appreciate you want to give me some credit but I don't wanna burden anyone. Therefor I am closing this PR. I am a big fan of Storybook, keep up the good work! |
@Bram-Zijp Follow up PR is here: |
Issue:
When using TypeScript and you specify props, the props were converted to optional. I don't think this is a good pattern. One should type cast if they want to go against specification. I don't see any reason to make it
Partial
in the code base (correct me if I'm wrong, this Partial seems intentional).What I did
Removed the
Partial
from the type defs in my node_modules folder and saw proper results. I searched this repo for references toBaseAnnotations
and found nothing concerning.How to test
Args
type to something else. If modules need to alter it, shouldn't they need to be the ones responsible for doing so? Needs to be verified though. This Partial was probably intentional -> YesExample